/**
 * 
 */
package com.googlecode.lohr.parser.combinators.matcher;


import java.util.Collection;

import com.googlecode.lohr.parser.combinators.MatchError;
import com.googlecode.lohr.parser.combinators.Parsers;
import com.googlecode.lohr.parser.combinators.node.AbstractNode;

/**
 * Like ChoiceMatcher, except it returns only the first match.
 * @author ted stockwell
 *
 */
public class FirstMatcher extends Matcher
{
	private final Matcher[] matchers;
	
	public FirstMatcher(Matcher[] matchers)
	{
		this.matchers= new Matcher[matchers.length];
		System.arraycopy(matchers, 0, this.matchers, 0, matchers.length);
	}
	public FirstMatcher(Collection<Matcher> matchers)
	{
		this.matchers= matchers.toArray(new Matcher[matchers.size()]);
	}

	@Override
	public void match(final MatchContext ctx, final int start) 
	{
		if (matchers.length <= 0) {
			ctx.matchComplete(this, start, new MatchError("This sequence contains no elements", start));
			return;
		}
		
		if (matchers.length <= 1) {
			new WrappedMatchTask(ctx, this, matchers[0], start).run();
			return;
		}
		
		final Matcher targetMatcher= this;
		final int targetStart= start;
		ctx.doMatch(matchers[0], start, new MatchListener() {
			@Override
			public void onMatchFound(final AbstractNode headNode) {
				ctx.matchFound(targetMatcher, targetStart, headNode);
				ctx.matchComplete(targetMatcher, targetStart);
			}
			
			@Override
			public void onMatchComplete(final MatchError headError) {
				if (!ctx.isComplete(targetMatcher, targetStart)) {
					ctx.doMatch(Parsers.first(Parsers.tail(matchers)), targetStart, new MatchListener() {
						public void onMatchFound(AbstractNode tailNode) {
							ctx.matchFound(targetMatcher, targetStart, tailNode);
							ctx.matchComplete(targetMatcher, targetStart);
						}
						
						@Override
						public void onMatchComplete(MatchError matchError) {
							ctx.matchComplete(targetMatcher, targetStart, matchError);
						}
					});
				}
			}
		});
	}

}